/**
 * @author mrdoob / http://mrdoob.com/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.GeometryLoader = function() {

  THREE.EventTarget.call( this );

  this.crossOrigin = null;
  this.path = null;


};

THREE.GeometryLoader.prototype = {

constructor: THREE.GeometryLoader,

load: function( url ) {

    var scope = this;
    var geometry = null;

    if ( scope.path == = null ) {

      var parts = url.split( '/' ); parts.pop();
      scope.path = ( parts.length < 1 ? '.' : parts.join( '/' ) );

    }

    //

    var xhr = new XMLHttpRequest();

    xhr.addEventListener( 'load', function( event ) {

      if ( event.target.responseText ) {

        geometry = scope.parse( JSON.parse( event.target.responseText ), monitor );

      } else {

scope.dispatchEvent( { type: 'error', message: 'Invalid file [' + url + ']' } );

      }

    }, false );

    xhr.addEventListener( 'error', function() {

scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } );

    }, false );

    xhr.open( 'GET', url, true );
    xhr.send( null );

    //

    var monitor = new THREE.LoadingMonitor();

    monitor.addEventListener( 'load', function( event ) {

scope.dispatchEvent( { type: 'load', content: geometry } );

    } );

    monitor.add( xhr );

  },

parse: function( data, monitor ) {

    var scope = this;
    var geometry = new THREE.Geometry();

    var scale = ( data.scale != = undefined ) ? 1 / data.scale : 1;

    // materials

    if ( data.materials ) {

      geometry.materials = [];

      for ( var i = 0; i < data.materials.length; ++ i ) {

        var m = data.materials[ i ];

        function isPow2( n ) {

          var l = Math.log( n ) / Math.LN2;
          return Math.floor( l ) == l;

        }

        function nearestPow2( n ) {

          var l = Math.log( n ) / Math.LN2;
          return Math.pow( 2, Math.round( l ) );

        }

        function createTexture( where, name, sourceFile, repeat, offset, wrap ) {

          where[ name ] = new THREE.Texture();
          where[ name ].sourceFile = sourceFile;

          if ( repeat ) {

            where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] );

            if ( repeat[ 0 ] != = 1 ) where[ name ].wrapS = THREE.RepeatWrapping;
            if ( repeat[ 1 ] != = 1 ) where[ name ].wrapT = THREE.RepeatWrapping;

          }

          if ( offset ) {

            where[ name ].offset.set( offset[ 0 ], offset[ 1 ] );

          }

          if ( wrap ) {

            var wrapMap = {

"repeat": THREE.RepeatWrapping,
"mirror": THREE.MirroredRepeatWrapping

            }

            if ( wrapMap[ wrap[ 0 ] ] != = undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ];
            if ( wrapMap[ wrap[ 1 ] ] != = undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ];

          }

          // load image

          var texture = where[ name ];

          var loader = new THREE.ImageLoader();
          loader.addEventListener( 'load', function( event ) {

            var image = event.content;

            if ( !isPow2( image.width ) || !isPow2( image.height ) ) {

              var width = nearestPow2( image.width );
              var height = nearestPow2( image.height );

              texture.image = document.createElement( 'canvas' );
              texture.image.width = width;
              texture.image.height = height;
              texture.image.getContext( '2d' ).drawImage( image, 0, 0, width, height );

            } else {

              texture.image = image;

            }

            texture.needsUpdate = true;

          } );
          loader.crossOrigin = scope.crossOrigin;
          loader.load( scope.path + '/' + sourceFile );

          if ( monitor ) monitor.add( loader );

        }

        function rgb2hex( rgb ) {

          return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255;

        }

        // defaults

        var mtype = "MeshLambertMaterial";
var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false };

        // parameters from model file

        if ( m.shading ) {

          var shading = m.shading.toLowerCase();

          if ( shading == = "phong" ) mtype = "MeshPhongMaterial";
          else if ( shading == = "basic" ) mtype = "MeshBasicMaterial";

        }

        if ( m.blending != = undefined && THREE[ m.blending ] != = undefined ) {

          mpars.blending = THREE[ m.blending ];

        }

        if ( m.transparent != = undefined || m.opacity < 1.0 ) {

          mpars.transparent = m.transparent;

        }

        if ( m.depthTest != = undefined ) {

          mpars.depthTest = m.depthTest;

        }

        if ( m.depthWrite != = undefined ) {

          mpars.depthWrite = m.depthWrite;

        }

        if ( m.vertexColors != = undefined ) {

          if ( m.vertexColors == "face" ) {

            mpars.vertexColors = THREE.FaceColors;

          } else if ( m.vertexColors ) {

            mpars.vertexColors = THREE.VertexColors;

          }

        }

        // colors

        if ( m.colorDiffuse ) {

          mpars.color = rgb2hex( m.colorDiffuse );

        } else if ( m.DbgColor ) {

          mpars.color = m.DbgColor;

        }

        if ( m.colorSpecular ) {

          mpars.specular = rgb2hex( m.colorSpecular );

        }

        if ( m.colorAmbient ) {

          mpars.ambient = rgb2hex( m.colorAmbient );

        }

        // modifiers

        if ( m.transparency ) {

          mpars.opacity = m.transparency;

        }

        if ( m.specularCoef ) {

          mpars.shininess = m.specularCoef;

        }

        if ( m.visible != = undefined ) {

          mpars.visible = m.visible;

        }

        if ( m.flipSided != = undefined ) {

          mpars.side = THREE.BackSide;

        }

        if ( m.doubleSided != = undefined ) {

          mpars.side = THREE.DoubleSide;

        }

        if ( m.wireframe != = undefined ) {

          mpars.wireframe = m.wireframe;

        }

        // textures

        if ( m.mapDiffuse ) {

          createTexture( mpars, "map", m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap );

        }

        if ( m.mapLight ) {

          createTexture( mpars, "lightMap", m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap );

        }

        if ( m.mapBump ) {

          createTexture( mpars, "bumpMap", m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap );

        }

        if ( m.mapNormal ) {

          createTexture( mpars, "normalMap", m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap );

        }

        if ( m.mapSpecular ) {

          createTexture( mpars, "specularMap", m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap );

        }

        // special case for normal mapped material

        if ( m.mapNormal ) {

          var shader = THREE.ShaderUtils.lib[ "normal" ];
          var uniforms = THREE.UniformsUtils.clone( shader.uniforms );

          uniforms[ "tNormal" ].value = mpars.normalMap;

          if ( m.mapNormalFactor ) {

            uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor );

          }

          if ( mpars.map ) {

            uniforms[ "tDiffuse" ].value = mpars.map;
            uniforms[ "enableDiffuse" ].value = true;

          }

          if ( mpars.specularMap ) {

            uniforms[ "tSpecular" ].value = mpars.specularMap;
            uniforms[ "enableSpecular" ].value = true;

          }

          if ( mpars.lightMap ) {

            uniforms[ "tAO" ].value = mpars.lightMap;
            uniforms[ "enableAO" ].value = true;

          }

          // for the moment don't handle displacement texture

          uniforms[ "uDiffuseColor" ].value.setHex( mpars.color );
          uniforms[ "uSpecularColor" ].value.setHex( mpars.specular );
          uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient );

          uniforms[ "uShininess" ].value = mpars.shininess;

          if ( mpars.opacity != = undefined ) {

            uniforms[ "uOpacity" ].value = mpars.opacity;

          }

var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
          var material = new THREE.ShaderMaterial( parameters );

        } else {

          var material = new THREE[ mtype ]( mpars );

        }

        if ( m.DbgName != = undefined ) material.name = m.DbgName;

        geometry.materials[ i ] = material;

      }

    }

    // geometry

    function isBitSet( value, position ) {

      return value & ( 1 << position );

    }

    var faces = data.faces;
    var vertices = data.vertices;
    var normals = data.normals;
    var colors = data.colors;
    var nUvLayers = 0;

    // disregard empty arrays

    if ( data.uvs ) {

      for ( var i = 0; i < data.uvs.length; i ++ ) {

        if ( data.uvs[ i ].length ) nUvLayers ++;

      }

    }

    for ( var i = 0; i < nUvLayers; i ++ ) {

      geometry.faceUvs[ i ] = [];
      geometry.faceVertexUvs[ i ] = [];

    }

    var offset = 0;
    var zLength = vertices.length;

    while ( offset < zLength ) {

      var vertex = new THREE.Vector3();

      vertex.x = vertices[ offset ++ ] * scale;
      vertex.y = vertices[ offset ++ ] * scale;
      vertex.z = vertices[ offset ++ ] * scale;

      geometry.vertices.push( vertex );

    }

    offset = 0;
    zLength = faces.length;

    while ( offset < zLength ) {

      var type = faces[ offset ++ ];

      var isQuad = isBitSet( type, 0 );

      var hasMaterial = isBitSet( type, 1 );
      var hasFaceUv = isBitSet( type, 2 );
      var hasFaceVertexUv = isBitSet( type, 3 );
      var hasFaceNormal = isBitSet( type, 4 );
      var hasFaceVertexNormal = isBitSet( type, 5 );
      var hasFaceColor = isBitSet( type, 6 );
      var hasFaceVertexColor = isBitSet( type, 7 );

      // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);

      if ( isQuad ) {

        var face = new THREE.Face4();

        face.a = faces[ offset ++ ];
        face.b = faces[ offset ++ ];
        face.c = faces[ offset ++ ];
        face.d = faces[ offset ++ ];

        var nVertices = 4;

      } else {

        var face = new THREE.Face3();

        face.a = faces[ offset ++ ];
        face.b = faces[ offset ++ ];
        face.c = faces[ offset ++ ];

        var nVertices = 3;

      }

      if ( hasMaterial ) {

        var materialIndex = faces[ offset ++ ];
        face.materialIndex = materialIndex;

      }

      // to get face <=> uv index correspondence

      var fi = geometry.faces.length;

      if ( hasFaceUv ) {

        for ( var i = 0; i < nUvLayers; i ++ ) {

          var uvLayer = data.uvs[ i ];

          var uvIndex = faces[ offset ++ ];

          var u = uvLayer[ uvIndex * 2 ];
          var v = uvLayer[ uvIndex * 2 + 1 ];

          geometry.faceUvs[ i ][ fi ] = new THREE.UV( u, v );

        }

      }

      if ( hasFaceVertexUv ) {

        for ( var i = 0; i < nUvLayers; i ++ ) {

          var uvLayer = data.uvs[ i ];

          var uvs = [];

          for ( var j = 0; j < nVertices; j ++ ) {

            var uvIndex = faces[ offset ++ ];

            var u = uvLayer[ uvIndex * 2 ];
            var v = uvLayer[ uvIndex * 2 + 1 ];

            uvs[ j ] = new THREE.UV( u, v );

          }

          geometry.faceVertexUvs[ i ][ fi ] = uvs;

        }

      }

      if ( hasFaceNormal ) {

        var normalIndex = faces[ offset ++ ] * 3;

        var normal = new THREE.Vector3();

        normal.x = normals[ normalIndex ++ ];
        normal.y = normals[ normalIndex ++ ];
        normal.z = normals[ normalIndex ];

        face.normal = normal;

      }

      if ( hasFaceVertexNormal ) {

        for ( i = 0; i < nVertices; i ++ ) {

          var normalIndex = faces[ offset ++ ] * 3;

          var normal = new THREE.Vector3();

          normal.x = normals[ normalIndex ++ ];
          normal.y = normals[ normalIndex ++ ];
          normal.z = normals[ normalIndex ];

          face.vertexNormals.push( normal );

        }

      }


      if ( hasFaceColor ) {

        var colorIndex = faces[ offset ++ ];

        face.color = new THREE.Color( colors[ colorIndex ] );

      }


      if ( hasFaceVertexColor ) {

        for ( var i = 0; i < nVertices; i ++ ) {

          var colorIndex = faces[ offset ++ ];

          face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) );

        }

      }

      geometry.faces.push( face );

    }


    // skin

    if ( data.skinWeights ) {

      for ( var i = 0, l = data.skinWeights.length; i < l; i += 2 ) {

        var x = data.skinWeights[ i ];
        var y = data.skinWeights[ i + 1 ];
        var z = 0;
        var w = 0;

        geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );

      }

    }

    if ( data.skinIndices ) {

      for ( var i = 0, l = data.skinIndices.length; i < l; i += 2 ) {

        var a = data.skinIndices[ i ];
        var b = data.skinIndices[ i + 1 ];
        var c = 0;
        var d = 0;

        geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );

      }

    }

    geometry.bones = data.bones;
    geometry.animation = data.animation;


    // morphing

    if ( data.morphTargets ) {

      for ( var i = 0, l = data.morphTargets.length; i < l; i ++ ) {

        geometry.morphTargets[ i ] = {};
        geometry.morphTargets[ i ].name = data.morphTargets[ i ].name;
        geometry.morphTargets[ i ].vertices = [];

        var dstVertices = geometry.morphTargets[ i ].vertices;
        var srcVertices = data.morphTargets [ i ].vertices;

        for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {

          var vertex = new THREE.Vector3();
          vertex.x = srcVertices[ v ] * scale;
          vertex.y = srcVertices[ v + 1 ] * scale;
          vertex.z = srcVertices[ v + 2 ] * scale;

          dstVertices.push( vertex );

        }

      }

    }

    if ( data.morphColors ) {

      for ( var i = 0, l = data.morphColors.length; i < l; i++ ) {

        geometry.morphColors[ i ] = {};
        geometry.morphColors[ i ].name = data.morphColors[ i ].name;
        geometry.morphColors[ i ].colors = [];

        var dstColors = geometry.morphColors[ i ].colors;
        var srcColors = data.morphColors [ i ].colors;

        for ( var c = 0, cl = srcColors.length; c < cl; c += 3 ) {

          var color = new THREE.Color( 0xffaa00 );
          color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] );

          dstColors.push( color );

        }

      }

    }

    geometry.computeCentroids();
    geometry.computeFaceNormals();

    return geometry;

  }

};
